package top.hjc.orange.repository.support;

import jakarta.persistence.EntityManager;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import top.hjc.orange.repository.OrangeJdbcRepository;

import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Optional;

/**
 * @author: huangjicheng
 * @date: 2025/10/10
 * @desc：
 */
public class OrangeJpaRepositoryImpl<T, ID>
        extends SimpleJpaRepository<T, ID>
        implements OrangeJdbcRepository<T, ID> {

    private final EntityManager entityManager;
    private final ProjectionFactory projectionFactory;

    public OrangeJpaRepositoryImpl(JpaEntityInformation<T, ID> entityInformation,
                                   EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
        this.projectionFactory = new SpelAwareProxyProjectionFactory();
    }

    @Override
    public <P> Optional<P> findById(ID id, Class<P> projectionType) {
        Optional<T> entity = super.findById(id);
        return entity.map(e -> convertToProjection(e, projectionType));
    }

    @SuppressWarnings("unchecked")
    private <P> P convertToProjection(T entity, Class<P> projectionType) {
        if (projectionType.isInterface()) {
            // 处理接口投影
            return projectionFactory.createProjection(projectionType, entity);
        } else {
            // 处理类投影 - 使用BeanUtils进行属性拷贝
            try {
                P projection = projectionType.getDeclaredConstructor().newInstance();
                BeanUtils.copyProperties(entity, projection);
                return projection;
            } catch (Exception e) {
                throw new RuntimeException("Failed to create projection", e);
            }
        }
    }

}